package com.example.myspringframework.aop.framework.autoproxy;

import com.example.myspringframework.aop.Advisor;
import com.example.myspringframework.beans.factory.BeanFactory;
import com.example.myspringframework.aop.AdvisedSupport;
import com.example.myspringframework.aop.ClassFilter;
import com.example.myspringframework.aop.Pointcut;
import com.example.myspringframework.aop.TargetSource;
import com.example.myspringframework.aop.aspectj.AspectJExpressionPointcutAdvisor;
import com.example.myspringframework.aop.framework.ProxyFactory;
import com.example.myspringframework.beans.BeansException;
import com.example.myspringframework.beans.factory.BeanFactoryAware;
import com.example.myspringframework.beans.factory.config.BeanPostProcessor;
import com.example.myspringframework.beans.factory.config.InstantiationAwareBeanPostProcessor;
import com.example.myspringframework.beans.factory.support.DefaultListableBeanFactory;
import org.aopalliance.aop.Advice;
import org.aopalliance.intercept.MethodInterceptor;

import java.util.Collection;
import java.util.HashSet;
import java.util.Set;

public class DefaultAdvisorAutoProxyCreator implements InstantiationAwareBeanPostProcessor, BeanFactoryAware {
    private DefaultListableBeanFactory beanFactory;

    // 存储提前暴露的beanName
    private final Set<String> earlyProxyReferences = new HashSet<>();

    /**
     * 如果是动态代理的基础类就不进行代理
     */
    private boolean isInfrastructureClass(Class<?> beanClass) {
        return Advice.class.isAssignableFrom(beanClass)
                || Pointcut.class.isAssignableFrom(beanClass)
                || Advisor.class.isAssignableFrom(beanClass);
    }

    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        this.beanFactory = (DefaultListableBeanFactory) beanFactory;
    }

    // 初始化之后执行
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        // 动态代理的bean之前暴露过就直接返回，没有暴露过就进行动态代理
        if (!this.earlyProxyReferences.contains(beanName)) {
            return wrapIfNecessary(bean, beanName);
        }
        return bean;
    }

    /**
     * 提前进行动态代理
     */
    @Override
    public Object getEarlyBeanReference(Object bean, String beanName) throws BeansException {
        this.earlyProxyReferences.add(beanName);
        return this.wrapIfNecessary(bean, beanName);
    }

    /**
     * 如果匹配上切点表达式就进行动态代理
     *
     * @param bean
     * @param beanName
     * @return
     */
    private Object wrapIfNecessary(Object bean, String beanName) {
        if (this.isInfrastructureClass(bean.getClass())) {
            return bean;
        }
        Collection<AspectJExpressionPointcutAdvisor> advisors = beanFactory.getBeansOfType(AspectJExpressionPointcutAdvisor.class).values();
        try {
            for (AspectJExpressionPointcutAdvisor advisor : advisors) {
                ClassFilter classFilter = advisor.getPointcut().getClassFilter();
                if (classFilter.matches(bean.getClass())) {
                    AdvisedSupport advisedSupport = new AdvisedSupport();
                    TargetSource targetSource = new TargetSource(bean);
                    advisedSupport.setTargetSource(targetSource);
                    advisedSupport.setMethodInterceptor((MethodInterceptor) advisor.getAdvice());
                    advisedSupport.setMethodMatcher(advisor.getPointcut().getMethodMatcher());
                    // 使用cglib动态代理
                    advisedSupport.setProxyTargetClass(true);
                    //返回代理对象
                    return new ProxyFactory(advisedSupport).getProxy();
                }
            }
        } catch (Exception ex) {
            throw new BeansException("Error create proxy bean for: " + beanName, ex);
        }
        return bean;
    }
}
